package com.fp.m7.client.presenter;

import net.customware.gwt.presenter.client.EventBus;
import net.customware.gwt.presenter.client.place.PlaceRequestEvent;
import net.customware.gwt.presenter.client.place.PlaceRequestHandler;
import net.customware.gwt.presenter.client.widget.WidgetDisplay;
import net.customware.gwt.presenter.client.widget.WidgetPresenter;

import com.fp.m7.client.AccountContext;
import com.fp.m7.shared.dto.AccountDto;
import com.fp.m7.shared.services.AccountServiceAsync;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HasWidgets;
import com.google.inject.Provider;

/**
 * An abstract class for the application's main presenter. Binded almost all presenter providers, sets up all presenters
 * if needed. Takes care of the place changes (via url tokens or events). Its display interface has methods for setting
 * header and main content. Whatsmore, it handles the display panel for infoEvents.
 * 
 * @author 7
 */
public abstract class AppPresenter<T extends AppPresenter.Display> extends WidgetPresenter<T> implements
        PlaceRequestHandler {

    public interface Display extends WidgetDisplay {

        void setHeader(WidgetDisplay display);

        void setContent(WidgetDisplay display);

        HasWidgets getInfoPanel();

    }

    protected AccountContext accountContext;

    protected final Provider<HeaderPublicPresenter> headerPublicPresenterProvider;
    protected final Provider<HeaderSecuredPresenter> headerSecuredPresenterProvider;
    private final Provider<LoginPresenter> loginPresenterProvider;
    private final Provider<RegisterPresenter> registerPresenterProvider;
    private final Provider<HomePresenter> homePresenterProvider;
    private final Provider<ProfilePresenter> profilePresenterProvider;
    private final Provider<NewCommentPresenter> newCommentPresenterProvider;
    private final Provider<TimelinePresenter> timelinePresenterProvider;
    private final Provider<SettingsPresenter> settingsPresenterProvider;
    private final Provider<InfoPresenter> infoPresenterProvider;

    protected HeaderPresenter<?> headerPresenter;
    private LoginPresenter loginPresenter;
    private RegisterPresenter registerPresenter;
    private HomePresenter homePresenter;
    private ProfilePresenter profilePresenter;
    private NewCommentPresenter newCommentPresenter;
    private TimelinePresenter timelinePresenter;
    private SettingsPresenter settingsPresenter;
    private InfoPresenter infoPresenter;

    protected AccountServiceAsync accountService;

    public AppPresenter(T display, EventBus eventBus, AccountContext accountContext,
            AccountServiceAsync accountService, //
            Provider<HeaderPublicPresenter> headerPublicPresenterProvider, //
            Provider<HeaderSecuredPresenter> headerSecuredPresenterProvider, //
            Provider<LoginPresenter> loginPresenterProvider, //
            Provider<RegisterPresenter> registerPresenterProvider, //
            Provider<HomePresenter> homePresenterProvider, //
            Provider<ProfilePresenter> profilePresenterProvider, //
            Provider<NewCommentPresenter> newCommentPresenterProvider, //
            Provider<TimelinePresenter> timelinePresenterProvider, //
            Provider<SettingsPresenter> settingsPresenterProvider, //
            Provider<InfoPresenter> infoPresenterProvider) {

        super(display, eventBus);

        this.accountContext = accountContext;
        this.accountService = accountService;

        this.headerPublicPresenterProvider = headerPublicPresenterProvider;
        this.headerSecuredPresenterProvider = headerSecuredPresenterProvider;
        this.loginPresenterProvider = loginPresenterProvider;
        this.registerPresenterProvider = registerPresenterProvider;
        this.homePresenterProvider = homePresenterProvider;
        this.profilePresenterProvider = profilePresenterProvider;
        this.newCommentPresenterProvider = newCommentPresenterProvider;
        this.timelinePresenterProvider = timelinePresenterProvider;
        this.settingsPresenterProvider = settingsPresenterProvider;
        this.infoPresenterProvider = infoPresenterProvider;
    }

    @Override
    protected void onBind() {
        eventBus.addHandler(PlaceRequestEvent.getType(), this);
    }

    @Override
    protected void onUnbind() {
        headerPresenter.unbind();
        if (headerPresenter != null) {
            headerPresenter.unbind();
        }
        if (loginPresenter != null) {
            loginPresenter.unbind();
        }
        if (registerPresenter != null) {
            registerPresenter.unbind();
        }
        if (homePresenter != null) {
            homePresenter.unbind();
        }
        if (profilePresenter != null) {
            profilePresenter.unbind();
        }
        if (newCommentPresenter != null) {
            newCommentPresenter.unbind();
        }
        if (timelinePresenter != null) {
            timelinePresenter.unbind();
        }
        if (settingsPresenter != null) {
            settingsPresenter.unbind();
        }
        if (infoPresenter != null) {
            infoPresenter.unbind();
        }
    }

    @Override
    protected void onRevealDisplay() {
        display.setHeader(getHeaderPresenter().getDisplay());

        getInfoPresenter().setContainer(display.getInfoPanel());

        if (isForAuthenticatedUser()) {
            fetchAuthenticatedAccount();
        }
    }

    protected abstract HeaderPresenter<?> getHeaderPresenter();

    protected LoginPresenter getLoginPresenter() {
        if (loginPresenter == null) {
            loginPresenter = loginPresenterProvider.get();
            loginPresenter.bind();
        }
        return loginPresenter;
    }

    protected RegisterPresenter getRegisterPresenter() {
        if (registerPresenter == null) {
            registerPresenter = registerPresenterProvider.get();
            registerPresenter.bind();
        }
        return registerPresenter;
    }

    protected HomePresenter getHomePresenter() {
        if (homePresenter == null) {
            homePresenter = homePresenterProvider.get();
            homePresenter.bind();
        }
        return homePresenter;
    }

    protected ProfilePresenter getProfilePresenter() {
        if (profilePresenter == null) {
            profilePresenter = profilePresenterProvider.get();
            profilePresenter.bind();
        }
        return profilePresenter;
    }

    protected TimelinePresenter getTimelinePresenter() {
        if (timelinePresenter == null) {
            timelinePresenter = timelinePresenterProvider.get();
            timelinePresenter.bind();
        }
        return timelinePresenter;
    }

    protected SettingsPresenter getSettingsPresenter() {
        if (settingsPresenter == null) {
            settingsPresenter = settingsPresenterProvider.get();
            settingsPresenter.bind();
        }
        return settingsPresenter;
    }

    protected NewCommentPresenter getNewCommentPresenter() {
        if (newCommentPresenter == null) {
            newCommentPresenter = newCommentPresenterProvider.get();
            newCommentPresenter.bind();
        }
        return newCommentPresenter;
    }

    protected InfoPresenter getInfoPresenter() {
        if (infoPresenter == null) {
            infoPresenter = infoPresenterProvider.get();
            infoPresenter.bind();
        }
        return infoPresenter;
    }

    public abstract boolean isForAuthenticatedUser();

    protected void fetchAuthenticatedAccount() {
        accountService.getAuthenticatedAccount(new AsyncCallback<AccountDto>() {
            @Override
            public void onSuccess(AccountDto loggedAccount) {
                accountContext.setAuthenticatedAccount(loggedAccount);
            }

            @Override
            public void onFailure(Throwable throwable) {
                accountContext.setAuthenticatedAccount(null);
            }
        });
    }

}
